<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/6/14
 * Time: 11:03
 */

namespace app\common;



use app\callback\Pay;
use app\Request;
use think\facade\Db;


class WechatPay
{
    private $device;
    private $appid;
    private $mch_id;
    private $key;
    private $trade_type;
    private $notify_url;
    private $openid;
    private $secret;
    private $body;
    private $attach;
    private $config;
    //https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
    function __construct($device = 'app',$trade_type = '',$openid = '',$attach = 'Pay')
    {
        $config = [
//            'APP'      => ['appid' => 'wx8598c19985700731','mch_id' => '1516310811','key' => '09cblnzcygc4jw4orzdewhq7jf4deif5','secret' => '123132'],
            'mobile'      => ['appid' => config('wechat')['mobile']['appid'],'mch_id' => config('wechat')['mobile']['mch_id'],'key' => config('wechat')['mobile']['key'],'secret' => config('wechat')['mobile']['secret']],
//            'mobile'   => ['appid' => 'wxaee08ccf4ef52692','mch_id' => '1550442771','key' => 'qK2fG0cJ1dA0aC0bF0jC0aW0aN1jQ1cA','secret' => 'ef752aafc963bec3534dd5397fc74adf'],
//            'mini'     => ['appid' => 'wx8598c19985700732','mch_id' => '1516310811','key' => '09cblnzcygc4jw4orzdewhq7jf4d2111','secret' => '123132']
        ];

        $this->device = $device;
        $this->appid = $config[$device]['appid'];
        $this->mch_id = $config[$device]['mch_id'];
        $this->key = $config[$device]['key'];
        $this->secret = $config[$device]['secret'];
        $this->trade_type = $trade_type;
        $this->openid = $openid;
        $this->body = '倍可瑞';
        $this->attach = $attach; //不需要啊
        $this->notify_url = url('Wechat/notifyurl',[],true,true);
        $this->config = $config;

    }

    public function getOpenid()
    {
        //通过code获得openid
        if (!isset($_GET['code'])){
            //触发微信返回code码
            $baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].$_SERVER['QUERY_STRING']);
            $baseUrl = urlencode(url('check_wechat', [], true, true)->build());
            ;
            $url = $this->__CreateOauthUrlForCode($baseUrl);
            Header("Location: $url");
            exit();
        } else {
            //获取code码，以获取openid
            $code = $_GET['code'];

            $openid = $this->getOpenidFromMp($code);
            return $openid;
        }
    }

    private function __CreateOauthUrlForCode($redirectUrl)
    {

        $urlObj["appid"] = $this->appid;
        $urlObj["redirect_uri"] = "$redirectUrl";
        $urlObj["response_type"] = "code";
        $urlObj["scope"] = "snsapi_base";
        $urlObj["state"] = "STATE"."&connect_redirect=1#wechat_redirect";
        $bizString = $this->ToUrlParams($urlObj);

        return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
    }

    public function GetOpenidFromMp($code)
    {
        $url = $this->__CreateOauthUrlForOpenid($code);
        //初始化curl
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //原错误curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,FALSE);
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//        if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
//            && WxPayConfig::CURL_PROXY_PORT != 0){
//            curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
//            curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
//        }
        //运行curl，结果以jason形式返回
        $res = curl_exec($ch);
        curl_close($ch);
        //取出openid
        $data = json_decode($res,true);
        $this->data = $data;
        $openid = $data['openid'];
        return $openid;
    }
    /**
     *
     * 构造获取open和access_toke的url地址
     * @param string $code，微信跳转带回的code
     *
     * @return 请求的url
     */
    private function __CreateOauthUrlForOpenid($code)
    {
        $urlObj["appid"] = $this->appid;
        $urlObj["secret"] = $this->secret;
        $urlObj["code"] = $code;
        $urlObj["grant_type"] = "authorization_code";
        $bizString = $this->ToUrlParams($urlObj);
        return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
    }

    private function ToUrlParams($urlObj)
    {
        $buff = "";
        foreach ($urlObj as $k => $v)
        {
            if($k != "sign"){
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    public function unifiedorder($out_trade_no = 0 ,$total_fee = 0)
    {


        if(empty($total_fee)) $total_fee = 1;
//        if($this->attach != 'Pay'){
//
//            //$out_trade_no = date('Ymdhis',time());//如果是充值
//        }else{ //支付的金额从订单里拿 Pay
//
//            $total_fee = db('order')->where('order_number',$out_trade_no)->sum('amount');
//        }

        $args = [
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
            'body' => $this->body,
            'attach' => $this->attach,
            'nonce_str' => $this->randomkeys(32),
            'out_trade_no' => $out_trade_no,
            'total_fee' => $total_fee,
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
            'notify_url' => $this->notify_url,
            'trade_type' => $this->trade_type,
        ];

        if($this->trade_type == "JSAPI"){
            $openid = $this->openid ? $this->openid : $this->getOpenid();
            $args = array_merge($args,['openid' => $openid]);
            //  array_push($args,['openid' => $openid]);
        }

        $query_str = $this->makeSignature($args, $this->key);

        parse_str($query_str['new_string'], $query_arr);//参数转数组 http_build_query//数组转参数

        $xml = $this->ToXml($query_arr);

        $ret_xml =  $this->http_request('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml);

        $ret = $this->FromXml($ret_xml);

        if($ret['result_code'] != 'SUCCESS'){  //result_code return_code
            return ['code'=>400,'msg'=>$ret['err_code_des']];
        }

//        $ret['timestamp'] = time();
        if($this->trade_type == 'JSAPI'){
            $args2 = [
                'appId' => $this->appid,
                'timeStamp' => (string)time(),
                'nonceStr' => $ret['nonce_str'],
                'package' => 'prepay_id='.$ret['prepay_id'],
                'signType' => 'MD5',
            ];
            $sign = $this->makeSignature($args2, $this->key);
            $args2['paySign'] = $sign['sign'];
        }

        if($this->trade_type == 'NATIVE'){
           return $ret;
        }
        if($this->trade_type == 'APP'){
            $args2 = [
                'appid' => $this->appid,
                'partnerid' => $this->mch_id,
                'prepayid' => $ret['prepay_id'],
                'package' => 'Sign=WXPay',
                'noncestr' => $ret['nonce_str'],
                'timestamp' => time(),
            ];
            $sign = $this->makeSignature($args2, $this->key);
            $args2['sign'] = $sign['sign'];

        }

        return ($args2);
    }

    public function native($out_trade_no = 0 ,$total_fee = 0)
    {


        if(empty($total_fee)) $total_fee = 1;
//        if($this->attach != 'Pay'){
//
//            //$out_trade_no = date('Ymdhis',time());//如果是充值
//        }else{ //支付的金额从订单里拿 Pay
//
//            $total_fee = db('order')->where('order_number',$out_trade_no)->sum('amount');
//        }

        $args = [
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
            'body' => $this->body,
            'attach' => $this->attach,
            'nonce_str' => $this->randomkeys(32),
            'out_trade_no' => $out_trade_no,
            'total_fee' => $total_fee,
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
            'notify_url' => $this->notify_url,
            'trade_type' => $this->trade_type,
        ];

        if($this->trade_type == "JSAPI"){
            $openid = $this->openid ? $this->openid : $this->getOpenid();
            $args = array_merge($args,['openid' => $openid]);
            //  array_push($args,['openid' => $openid]);
        }

        $query_str = $this->makeSignature($args, $this->key);

        parse_str($query_str['new_string'], $query_arr);//参数转数组 http_build_query//数组转参数

        $xml = $this->ToXml($query_arr);

        $ret_xml =  $this->http_request('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml);

        $ret = $this->FromXml($ret_xml);
        if($ret['return_code'] != 'SUCCESS'){
            return ['code'=>400,'msg'=>$ret['return_msg']];
        }
//        $ret['timestamp'] = time();
        if($this->trade_type == 'JSAPI'){
            $args2 = [
                'appId' => $this->appid,
                'timeStamp' => (string)time(),
                'nonceStr' => $ret['nonce_str'],
                'package' => 'prepay_id='.$ret['prepay_id'],
                'signType' => 'MD5',
            ];
            $sign = $this->makeSignature($args2, $this->key);
            $args2['paySign'] = $sign['sign'];
        }
        if($this->trade_type == 'APP'){
            $args2 = [
                'appid' => $this->appid,
                'partnerid' => $this->mch_id,
                'prepayid' => $ret['prepay_id'],
                'package' => 'Sign=WXPay',
                'noncestr' => $ret['nonce_str'],
                'timestamp' => time(),
            ];
            $sign = $this->makeSignature($args2, $this->key);
            $args2['sign'] = $sign['sign'];

        }

        return ($args2);
    }



    /**
     * 微信回调
     */
    public function notifyurl()
    {

        //$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
        $xml = file_get_contents('php://input');
        paylog('======== wechat start =========');

        //$xml = file_get_contents('php://input');
//        $xml = '<xml><return_code><![CDATA[SUCCESS]]></return_code>
//<return_msg><![CDATA[OK]]></return_msg>
//<appid><![CDATA[wx8598c19985700731]]></appid>
//<mch_id><![CDATA[1516310811]]></mch_id>
//<nonce_str><![CDATA[lGNLvSSwsPsK9Rer]]></nonce_str>
//<sign><![CDATA[D3309ED89CD4F6470F34F623F6C92F57]]></sign>
//<result_code><![CDATA[SUCCESS]]></result_code>
//<openid><![CDATA[o5408036qRcpnUXLmZtO5ksKwZbA]]></openid>
//<is_subscribe><![CDATA[N]]></is_subscribe>
//<trade_type><![CDATA[APP]]></trade_type>
//<bank_type><![CDATA[CFT]]></bank_type>
//<total_fee>1</total_fee>
//<fee_type><![CDATA[CNY]]></fee_type>
//<transaction_id><![CDATA[4200000417201909090975026289]]></transaction_id>
//<out_trade_no><![CDATA[201909091656235328836903]]></out_trade_no>
//<attach><![CDATA[]]></attach>
//<time_end><![CDATA[20190909165706]]></time_end>
//<trade_state><![CDATA[SUCCESS]]></trade_state>
//<cash_fee>1</cash_fee>
//<trade_state_desc><![CDATA[支付成功]]></trade_state_desc>
//<cash_fee_type><![CDATA[CNY]]></cash_fee_type>
//</xml>';

        $data = $this->FromXml($xml);

        $status = Db::name('unifiedorder')->where('order_number',$data['out_trade_no'])->value('status');
        if($status != 1){ //待支付可以继续
            die;
        }
        Db::name('unifiedorder')->where('order_number',$data['out_trade_no'])->where('status',1)->update(['status'=>2]);
        paylog('=========== wechat start ===========');
        paylog($data);

//        $data = array (
//            'appid' => 'wx700e4375f498d634',
//            'attach' => 'recharge',
//            'bank_type' => 'OTHERS',
//            'cash_fee' => '1',
//            'fee_type' => 'CNY',
//            'is_subscribe' => 'N',
//            'mch_id' => '1616861761',
//            'nonce_str' => 'qtq4YpvvBbi8sXG4L8pOrjze7Q5dbd5J',
//            'openid' => 'oPuAl6MGRbjNb4X2-4UsexhLt3Y4',
//            'out_trade_no' => '202205111037215190805639',
//            'result_code' => 'SUCCESS',
//            'return_code' => 'SUCCESS',
//            'sign' => '4A7A72050519A9D61A99131B0A0A446D',
//            'time_end' => '20220511103730',
//            'total_fee' => '1',
//            'trade_type' => 'APP',
//            'transaction_id' => '4200001417202205114805604176',
//        );


        $args = [
            //            'bank_type' => $data['bank_type'],
            //            'cash_fee' => $data['35000'],
            //            'is_subscribe' => $data['N'],
            'appid' => $data['appid'],
            'mch_id' => $data['mch_id'],
            'openid' => $this->openid,
            //            'body' => $data['body'],
            //            'attach' => $data['attach'],
            'nonce_str' => $data['nonce_str'],
            'out_trade_no' => $data['out_trade_no'],
            'total_fee' => $data['total_fee'],
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
            'notify_url' => $this->notify_url,
            'trade_type' => $data['trade_type'],
        ];

        foreach ($this->config as $v) {
            if ($data['appid'] == $v['appid']) {
                $key = $v['key'];
            }
        }
//            dump($this->makeSignature($data, $key)['sign'] . '-' . $data['sign']);
//            paylog('sign make and data='.$this->makeSignature($data, $key)['sign'] . '-' . $data['sign'],'wechat');

        if ($this->makeSignature($data, $key)['sign'] == $data['sign'] && $data['result_code'] == 'SUCCESS' && $data['return_code'] == 'SUCCESS') {
            paylog('验签ok');
            $data['pay_action'] = $data['attach']; //支付方式
            $data['pay_type'] = '微信'; //支付类型
            (new Pay())->business_logic($data);
        }
    }

    public function randomkeys($length)
    {
        $returnStr = '';
        $pattern = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        for ($i = 0; $i < $length; $i++) {
            $returnStr .= $pattern[mt_rand(0, 61)];
        }
        return $returnStr;
    }

    public function makeSignature($args, $key,$sign_type='md5')
    {
        ksort($args);
        $stringA = '';
        $stringSignTemp = '';
        foreach ($args as $k => $v) {
            if($k != 'sign' && !empty($v)){
                $stringA .= $k . '=' . $v . '&';
            }
            //$stringA .= $k . '=' . $v . '&';
        }
        //       $stringA appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA&
        $stringSignTemp = $stringA . 'key=' . $key;
        //$stringSignTemp appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA&key=192006250b4c09247ec02edce69f6a2d
//        halt($stringSignTemp);
        if($sign_type == 'md5'){
            $signature = strtoupper(md5($stringSignTemp));
        }

        if($sign_type == 'hamc'){
            $signature = strtoupper(hash_hmac("sha256", $stringSignTemp, $key));
        }

        //$signature 9A0A8659F005D6984697E2CA0A9CF3B7

        $newString = $stringA . 'sign=' . $signature;

        //$newSign = base64_encode($newString);
        return ['sign' => $signature,'new_string' => $newString];
        return $newString;
    }

    public function ToXml($array)
    {

        $xml = "<xml>";
        foreach ($array as $key=>$val)
        {
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }
    public function FromXml($xml)
    {
        if(!$xml){
            return ['code' => 400 ,'message' => 'xml数据异常'];
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);

    }

    /**
     * 通过微信api进行退款流程 唯一对外接口
     * @return string
     */
    public function refund($order_number,$refund_order,$total_price,$refund_price)
    {

        $parma = array(
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
            'nonce_str' => $this->randomkeys(32),//这个是随机数 自己封装去吧。。。
            'out_refund_no' => $refund_order,
            'out_trade_no' => $order_number,
            'total_fee' => $total_price*100,
            'refund_fee' =>$refund_price*100
        );
        $parma['sign'] = $this->makeSignature($parma, $this->key)['sign'];
        $xmldata = $this->ToXml($parma);
        $xmlresult = $this->postXmlSSLCurl($xmldata, 'https://api.mch.weixin.qq.com/secapi/pay/refund');

        $result = $this->FromXml($xmlresult);

        if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
            return true;
        }else{
            return $result['err_code_des'];
        }

//        return $result;
    }

    public function add_user()
    {
        $parma = array(
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
            'nonce_str' => $this->randomkeys(32),//这个是随机数 自己封装去吧。。。
            'sign_type'=> 'HMAC-SHA256',
            'receiver'=> json_encode([
                'type'=>'PERSONAL_OPENID',
                'account'=> 'oXXkm40X45SdJsfj_Es7qyGT7CmA',
                'relation_type' => 'SUPPLIER',
            ])
        );

        $parma['sign'] = $this->makeSignature($parma, $this->key,'hamc')['sign'];

        $xmldata = $this->ToXml($parma);

        $xmlresult = $this->postXmlSSLCurl($xmldata, 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver');

        $result = $this->FromXml($xmlresult);
        var_dump($result);die;
        return $result;
    }

    /*https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
 * 企业提现到零钱
 */
    public function withdraw($order_number,$openid,$brokerage)
    {
        $parma = [
            'mch_appid' => $this->appid,
            'mchid' => $this->mch_id,
            'nonce_str' => $this->randomkeys(32),//这个是随机数 自己封装去吧。。。
            'partner_trade_no'=> $order_number,
            'openid'=> $openid,
            'check_name'=>'NO_CHECK',
            'amount'=>$brokerage*100,
            'desc'=> cache('web_name').'工作奖励:'.$order_number,

        ];

        $parma['sign'] = $this->makeSignature($parma, $this->key,'md5')['sign'];
        ;
        $xmldata = $this->ToXml($parma);


        $xmlresult = $this->postXmlSSLCurl($xmldata, 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers');

        $result = $this->FromXml($xmlresult);

        if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
            return true;
        }else{
            return $result['err_code_des'];
        }

    }

    /*
     * 提现订单查询
     */
    public function withdraw_check($order_number,$openid){
        $parma = [
            'appid' => $this->appid,
            'mch_id' => $this->mch_id,
            'nonce_str' => $this->randomkeys(32),//这个是随机数 自己封装去吧。。。
            'partner_trade_no'=> $order_number
        ];
        $parma['sign'] = $this->makeSignature($parma, $this->key,'md5')['sign'];
        $xmldata = $this->ToXml($parma);
        $xmlresult = $this->postXmlSSLCurl($xmldata, 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo');
        $result = $this->FromXml($xmlresult);
        if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
            return true;
        }else{
            return $result['err_code_des'];
        }
    }

    protected function http_request($url, $data = null)
    {

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($data)){
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }

    /**
     * 需要使用证书的请求
     * @param $xml
     * @param $url
     * @param int $second
     * @return bool|mixed
     */
    protected function postXmlSSLCurl($xml, $url, $second = 30)
    {
        $cert = root_path().'config/cert/apiclient_cert.pem';
        $key = root_path().'config/cert/apiclient_key.pem';
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $cert);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY,$key) ;
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $data = curl_exec($ch);
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "curl出错，错误码:$error" . "<br>";
            curl_close($ch);
            return false;
        }
    }

}